﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Grain.Crm.DataMigrator.CrmServices;
using Grain.Crm.DataMigrator.Infrastructure;

namespace Grain.Crm.DataMigrator.Imports
{
    public class ImportsHelper
    {
        public static bool CheckImportResult(CrmService service, importfile file, out IEnumerable<importlog> errLogs)
        {
            ColumnSet cols = new ColumnSet();
            errLogs = new List<importlog>();

            QueryByAttribute query = new QueryByAttribute();
            query.EntityName = EntityName.importlog.ToString();
            query.Attributes = new string[] { "importfileid" };
            query.Values = new object[] { file.importfileid.Value.ToString() };
            cols.Attributes = new string[] { "errordescription", "additionalinfo", "columnvalue", "headercolumn", "statecode", "statuscode", "errornumber", "logphasecode", "sequencenumber" };
            query.ColumnSet = cols;

            Console.WriteLine("Checking for import error logs..");

            BusinessEntityCollection results = service.RetrieveMultiple(query);
            if (results.BusinessEntities.Count() > 0)
            {
                Console.WriteLine("  There are import errors:");
                errLogs = results.BusinessEntities.Cast<importlog>();

                foreach (var log in errLogs)
                {
                    Console.WriteLine("    error description: " + log.errordescription);
                }

                return false;
            }
            else
            {
                Console.WriteLine("  No errors logged.");
                return true;
            }
        }

        public static void WaitForImportCompletion(CrmService service, ImportRecordsImportResponse response)
        {
            Console.WriteLine("Waiting for server to process import");

            var cols = new ColumnSet();
            cols.Attributes = new string[] { "statecode" };
            for (int i = 0; i < 50; i++)
            {
                Console.Write(".");
                System.Threading.Thread.Sleep(500);
                var op = (asyncoperation)service.Retrieve(EntityName.asyncoperation.ToString(), response.AsyncOperationId, cols);
                if (op.statecode.Value == AsyncOperationState.Completed)
                {
                    break;
                }
            }
            Console.WriteLine("Done.");

        }

        public static void PrepareImport(
            CrmService service, WhoAmIResponse whoAmIResponse,
            bool entityExists, string targetEntity, string filename,
            string fileContent, string headerRow,
            out Guid importId, out importfile file)
        {

            var imp = new import();
            imp.isimport = new CrmBoolean();
            imp.isimport.Value = true;

            // Note: ImportModeCode 0 = "Create" , 1 = update
            imp.modecode = new Picklist();
            if (entityExists)
            {
                imp.modecode.Value = 1;
                Console.WriteLine("Entity already exists. Updating..");
            }
            else
            {
                imp.modecode.Value = 0;
                Console.WriteLine("Entity doesn't exist. Creating new..");

            }
            imp.name = "Importing " + targetEntity;
            imp.sendnotification = new CrmBoolean();
            imp.sendnotification.Value = false;

            importId = service.Create(imp);

            file = new importfile();
            file.content = headerRow + Environment.NewLine + fileContent;
            file.headerrow = headerRow;
            file.targetentityname = targetEntity;
            file.name = "Import File";
            file.isfirstrowheader = new CrmBoolean() { Value = true };
            file.recordsownerid = new Lookup() { name = "systemuser", Value = whoAmIResponse.UserId };
            file.source = filename;
            file.sourceentityname = "Excel sheet";
            file.importid = new Lookup { name = "import", Value = importId };

            // Note: Field delimiter 2 = comma;
            file.fielddelimitercode = new Picklist() { Value = 2 };

            // Note: Data Delimiter 1 = double quote
            file.datadelimitercode = new Picklist() { Value = 1 };
            file.size = file.content.Length.ToString();

            // Note: Process code 1 = "Process"
            file.processcode = new Picklist() { Value = 1 };
            file.usesystemmap = new CrmBoolean() { Value = true };
        }

        public static BusinessEntity GetEntity(ICrmService service, string entityName, string[] searchAttributes, string[] searchAttributesValues, string[] queryAttributes)
        {
            ColumnSet cols = new ColumnSet();
            QueryByAttribute query = new QueryByAttribute();
            query.EntityName = entityName;

            query.Attributes = searchAttributes;
            query.Values = searchAttributesValues;

            cols.Attributes = queryAttributes;
            query.ColumnSet = cols;

            BusinessEntityCollection results = service.RetrieveMultiple(query);

            if (results.BusinessEntities.Count() > 1)
                throw new InvalidOperationException("More than one entities found");
            else if (results.BusinessEntities.Count() == 0)
                return null;

            return results.BusinessEntities.First();
        }

        public static string GetData(ICrmService service, string entityName, string[] searchAttributes, string[] searchAttributesValues, string queryAttribute)
        {
            var entity = GetEntity(service, entityName, searchAttributes, searchAttributesValues,
                                       new[] { queryAttribute });

            var property = entity.GetType().GetProperty(queryAttribute);
            if (property == null)
                throw new ArgumentException("Attribute '" + queryAttribute + "' does not exist in entity '" + entityName + "'");

            var value = property.GetValue(entity, null);

            if (value is string)
                return (string)value;

            if (value is Key)
                return (value as Key).Value.ToString();

            if (value is Picklist)
                return (value as Picklist).name;


            return value.ToString();
        }

        public static Guid? GetEntityId(ICrmService service, string entityName, IEnumerable<SearchAttribute> searchAttributes)
        {
            var attributes = searchAttributes.Select(a => a.Name).ToArray();
            var values = searchAttributes.Select(a => a.Value.Resolve()).ToArray();

            return GetEntityId(service, entityName, attributes, values);
        }

        public static QueryByAttribute CreateQuery(string entityName, string[] searchAttributes, string[] searchAttributesValues)
        {
            ColumnSet cols = new ColumnSet();
            QueryByAttribute query = new QueryByAttribute();
            query.EntityName = entityName;

            query.Attributes = searchAttributes;
            query.Values = searchAttributesValues;

            string keyAttribute;

            if (entityName == EntityName.contact.ToString())
                keyAttribute = "contactid";
            else if (entityName == EntityName.new_curriculumvitae.ToString())
                keyAttribute = "new_curriculumvitaeid";
            else if (entityName == EntityName.new_language.ToString())
                keyAttribute = "new_languageid";
            else if (entityName == EntityName.new_contactlanguage.ToString())
                keyAttribute = "new_contactlanguageid";
            else if (entityName == EntityName.new_contactskill.ToString())
                keyAttribute = "new_contactskillid";
            else if (entityName == EntityName.new_skill.ToString())
                keyAttribute = "new_skillid";
            else
                throw new InvalidOperationException("CreateQuery: entity '" + entityName + "' not supported");

            cols.Attributes = new string[] { keyAttribute };
            query.ColumnSet = cols;

            return query;
        }

        public static Guid? GetEntityId(ICrmService service, string entityName, string[] searchAttributes, string[] searchAttributesValues)
        {
            var query = CreateQuery(entityName, searchAttributes, searchAttributesValues);

            BusinessEntityCollection results = service.RetrieveMultiple(query);

            if (results.BusinessEntities.Count() > 1)
                throw new InvalidOperationException("More than one entities found");
            else if (results.BusinessEntities.Count() == 0)
                return null;

            if (entityName == EntityName.contact.ToString())
                return ((contact)results.BusinessEntities.First()).contactid.Value;
            else if (entityName == EntityName.new_curriculumvitae.ToString())
                return ((new_curriculumvitae)results.BusinessEntities.First()).new_curriculumvitaeid.Value;
            else if (entityName == EntityName.new_language.ToString())
                return ((new_language)results.BusinessEntities.First()).new_languageid.Value;
            else if (entityName == EntityName.new_contactlanguage.ToString())
                return ((new_contactlanguage)results.BusinessEntities.First()).new_contactlanguageid.Value;
            else if (entityName == EntityName.new_contactskill.ToString())
                return ((new_contactskill)results.BusinessEntities.First()).new_contactskillid.Value;
            else if (entityName == EntityName.new_skill.ToString())
                return ((new_skill)results.BusinessEntities.First()).new_skillid.Value;

            else
                throw new InvalidOperationException("GetEntityId: entity '" + entityName + "' not supported");


        }

        public static void AddKeyValueToContent(Guid entityId, string targetEntity, ref string headerRow, ref string fileContent)
        {
            if (targetEntity == EntityName.contact.ToString())
            {
                headerRow += ",Contact";
                fileContent += "," + entityId;
            }
            else if (targetEntity == EntityName.new_curriculumvitae.ToString())
            {
                headerRow += ",Curriculum Vitae";
                fileContent += "," + entityId;
            }
            else if (targetEntity == EntityName.new_contactlanguage.ToString())
            {
                headerRow += ",ContactLanguage";
                fileContent += "," + entityId;

            }
            else if (targetEntity == EntityName.new_contactskill.ToString())
            {
                headerRow += ",Contact Skill";
                fileContent += "," + entityId;

            }
            else
                throw new InvalidOperationException("AddKeyValueToContent: entity '" + targetEntity + "' not supported");
        }


        public static ImportRecordsImportResponse PerformImport(CrmService service, importfile file, Guid importId)
        {
            var importfileId = service.Create(file);
            file.importfileid = new Key() { Value = importfileId };

            // Parse the import.
            var parseRequest = new ParseImportRequest();
            parseRequest.ImportId = importId;
            service.Execute(parseRequest);

            // Transform the import.
            var transRequest = new TransformImportRequest();
            transRequest.ImportId = importId;
            service.Execute(transRequest);

            // Create an ImportRecordsImport request
            var request = new ImportRecordsImportRequest();

            // Assign the request the id of the import we want to begin
            request.ImportId = importId;

            // Execute the request.
            var response = (ImportRecordsImportResponse)service.Execute(request);

            return response;
        }

    }
}
